#include "UdpSocket.hpp"
#include "hiScopeGuard.h"
#include <string.h>
#include <uv.h>
#include "DepLibUV.hpp"
#include <cstring>
#include<stdio.h>
#include "common/HiCommonLog.hpp"

static void onSend(uv_udp_send_t *req, int status) {
	HI_FUNC_DEBUG();
	if (nullptr == req || nullptr == req->data) {
		return;
	}
  auto *sendData = static_cast<UdpSocket::UvSendData *>(req->data);
  UdpSocket *socket = sendData->socket;

  // Delete the UvSendData struct (which includes the uv_req_t and the store
  // char[]).
  std::free(sendData);

  // Just notify the UdpSocket when error.
  if (status != 0)
    socket->OnUvSendError(status);
}

void UdpSocket::OnUvSendError(int reason) {
	HI_DEBUG("UdpSocket::OnUvSendError , error:%s\n", uv_strerror(reason));
	this->onSendFailed(reason);
}

void UdpSocket::Send(const uint8_t *data, size_t len, const char* ip, uint16_t port) {
	HI_DEBUG("UdpSocket::Send start, ip:%s, port:%d\n", ip, port);
	if (this->isClosing) {
		HI_DEBUG("UdpSocket::Send failed, socket is closing\n");
		return;
	}
	if (len == 0) {		
		return;
	}

	
	// clang-format off
	struct sockaddr_storage addr{};
	
	int err = uv_ip4_addr(ip, static_cast<int>(port), reinterpret_cast<struct sockaddr_in *>(&addr));
	if (err != 0) {
		HI_DEBUG("UdpSocket::Send failed, error:%s\n", uv_strerror(err));
		return;
	}
	Send(data, len, reinterpret_cast<struct sockaddr *>(&addr));
}

void UdpSocket::Send(const uint8_t *data, size_t len,
                     const struct sockaddr *addr) {
	HI_FUNC_DEBUG();
	if (this->isClosing) {
		HI_DEBUG("UdpSocket::Send failed, socket is closing\n");
		return;
	}

	if (len == 0)
		return;

	// First try uv_udp_try_send(). In case it can not directly send the datagram
	// then build a uv_req_t and use uv_udp_send().

	uv_buf_t buffer = uv_buf_init(reinterpret_cast<char *>(const_cast<uint8_t *>(data)), len);
	int sent = uv_udp_try_send(this->uvHandle, &buffer, 1, addr);

	// Entire datagram was sent. Done.
	if (sent == static_cast<int>(len)) {
		// Update sent bytes.
		this->sentBytes += sent;
		HI_DEBUG("UdpSocket::Send byte:%d(full message)\n", sent);
		return;
	}
	if (sent >= 0) {
		// Update sent bytes.
		this->sentBytes += sent;
		HI_DEBUG("UdpSocket::Send byte:%d(a part of this message)\n", sent);
		return;
	}
	// Error,
	if (sent != UV_EAGAIN) {
		HI_DEBUG("UdpSocket::Send error, sent != UV_EAGAIN\n");
		return;
	}
	// Otherwise UV_EAGAIN was returned so cannot send data at first time. Use
	// uv_udp_send().

	// MS_DEBUG_DEV("could not send the datagram at first time, using
	// uv_udp_send() now");

	// Allocate a special UvSendData struct pointer.
	auto *sendData = static_cast<UvSendData *>(std::malloc(sizeof(UvSendData) + len));

	HiScopeGuard scope_sendData([&](){	
		if (nullptr == sendData) {
			return;
		}
		std::free(sendData);
		sendData = nullptr;
	});

	sendData->socket = this;
	std::memcpy(sendData->store, data, len);
	sendData->req.data = (void *)sendData;

	buffer = uv_buf_init(reinterpret_cast<char *>(sendData->store), len);

	int err = uv_udp_send(&sendData->req, this->uvHandle, &buffer, 1, addr,
					static_cast<uv_udp_send_cb>(::onSend));
	if (err != 0) {
		HI_DEBUG("UdpSocket::Send failed, error:%s\n", uv_strerror(err));
		return;
	}
	// Update sent bytes.
	this->sentBytes += len;
	scope_sendData.dismissed();
}